import {
  Component,
  OnInit,
  HostBinding,
  Input,
  HostListener,
  TemplateRef,
  ElementRef,
  AfterContentInit,
  ContentChildren,
  QueryList,
} from '@angular/core';
import {animate, state, style, transition, trigger} from '@angular/animations';

import { OverlayEntry, POSITION_MAP } from '../shared/shared';

import { MenuComponent, MenuMode } from './menu.component';
import { MenuStateService } from './menu-state.service';
import { combineLatest } from 'rxjs';

let ID = 1;

const SHOW_HIDE_DELAY = 200;

@Component({
  selector: 'fui-sub-menu',
  templateUrl: './sub-menu.component.html',
  animations: [
    trigger('showHide', [
      state('true', style({
        transform: 'scaleY(1)',
        transformOrigin: '0 0',
        overflow: 'hidden',
      })),
      state('void', style({
        opacity: 0,
        transform: 'scaleY(0.8)',
        transformOrigin: '0 0',
        height: '0px',
        overflow: 'hidden',
      })),
      transition('void => true', [
        animate(`${SHOW_HIDE_DELAY}ms cubic-bezier(0.23, 1, 0.32, 1)`),
      ]),
    ]),
  ],
})
export class SubMenuComponent extends OverlayEntry implements OnInit, AfterContentInit {
  @HostBinding('class.fui-sub-menu') hostClass = true;

  // 是否折叠
  @HostBinding('class.collapsed') get collapsedClass() {
    return this.collapsed;
  }

  @ContentChildren(SubMenuComponent, { descendants: true }) subMenuChildren: QueryList<SubMenuComponent>;

  /** Sub menu icon. */
  @Input() icon: string;

  /** Sub menu avatar text. */
  @Input() avatarText: string;

  /** Background style. */
  @Input() background: string;

  /** Menu title. */
  @Input() menuTitle: string;

  /** Optional menu title template. */
  @Input() menuTitleTemplate: TemplateRef<{$implicit: boolean; collapsed: boolean; active: boolean}>;

  /**
   * Align the sub menu to a certain component.
   * Only supported in 'float' mode.
   */
  @Input() alignTo: {elementRef: ElementRef};

  /**  Menu key.  */
  @Input()
  set key(value: string) {
    this._key = value;
    if (value !== null && value !== undefined) {
      this.hasInputKey = true;
    }
  }
  get key() {
    return this._key;
  }

  menuKeys: string[] = [];

  // 用于submenu的唯一标识符
  private _key = `SUBMENU_${ID ++}`;

  // 标识是否传入了key, 若传入则可选中。
  hasInputKey = false;

  /** head是否被hover */
  hovered: boolean;

  /** float区域是否被hover */
  floatHovered: boolean;

  state: MenuStateService;

  @HostListener('mouseenter') mouseenter() {
    this.hovered = true;
    if (this.isFloatChildren) {
      this.showFloatChildren();
    }
  }

  @HostListener('mouseleave') mouseleave() {
    this.hovered = false;
    if (this.isFloatChildren) {
      this.hideFloatChildren();
    }
  }

  get collapsed() {
    return this.menu.collapsed;
  }

  get isFloatChildren() {
    return this.mode === 'float' || this.collapsed;
  }

  get childrenCollapsed() {
    return this.collapsed && this.mode === 'foldable';
  }

  // 是否高亮，当子menu-item被选中时高亮
  get active() {
    return this.state.isSubmenuActive(this.key)
      || (this.hasInputKey && this.state.isActive(this.key))
      || (this.subMenuChildren.length > 0 && this.subMenuChildren.filter((child) => child._key !== this._key)
        .some((child) => child.active));
  }

  get open() {
    return this.state.submenuOpens[this.key];
  }

  get context() {
    return {
      $implicit: this.collapsed,
      collapsed: this.collapsed,
      active: this.active,
    };
  }

  get mode(): MenuMode {
    return this.menu.mode;
  }

  get headPaddingRight() {
    if (this.collapsed) {
      return 0;
    } else if (this.mode === 'float') {
      return 16;
    } else {
      return 40;
    }
  }

  get childrenHovered() {
    return this.subMenuChildren.length > 0 && this.subMenuChildren.filter((child) => child._key !== this._key)
      .some((child) => child.floatHovered || child.childrenHovered);
  }

  constructor(
    public elementRef: ElementRef,
    public menu: MenuComponent,
    public menuState: MenuStateService,
  ) {
    super();
    this.state = menuState;
  }

  ngOnInit() {
    this.state.submenuOpens[this.key] = false;
    this.state.registerSubmenu(this.key);

    const dirs = ['rightTop', 'rightBottom'];
    this.positions = dirs.map((dir) => POSITION_MAP[dir]);
    if (this.alignTo) {
      this.setOverlayOrigin(this.alignTo);
    } else {
      this.setOverlayOrigin(this);
    }
  }

  ngAfterContentInit() {
    if (this.isFloatChildren && this.subMenuChildren.length > 0) {
      combineLatest(this.subMenuChildren.map((child) => child.visible))
      .subscribe((visibles) => {
        if (!visibles.some((visible) => visible) && !this.floatHovered) {
          this.hide();
        }
      });
    }
  }

  toggleOpen() {
    if (this.hasInputKey) {
      this.state.select(this.key);
    }
    this.state.submenuOpens[this.key] = !this.state.submenuOpens[this.key];
  }

  showFloatChildren() {
    setTimeout(() => {
      if (!this.hovered) { // Cancel showing if not hovering
        return;
      }
      const visible = this.visible.getValue();
      if (!visible) {
        this.show();
      }
    }, SHOW_HIDE_DELAY);
  }

  hideFloatChildren() {
    setTimeout(() => {
      if (this.hovered || this.floatHovered || this.childrenHovered) { // Cancel hiding if still hovering
        return;
      }
      const visible = this.visible.getValue();
      if (visible) {
        this.hide();
      }
    }, SHOW_HIDE_DELAY);
  }

  mouseenterFloat() {
    this.floatHovered = true;
  }

  mouseleaveFloat() {
    this.floatHovered = false;
    this.hideFloatChildren();
  }
}
